home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / pxsrc_21.zip / POLYXARC.C < prev    next >
Text File  |  1991-04-14  |  61KB  |  1,633 lines

  1. /*
  2.  
  3.     PolyXarc: de-archives most archive formats (with the help of the
  4.     de-archive programs).
  5.  
  6.  */
  7. #define R_VERS    "2.1"
  8. #define R_DATE    "5 April 1991"
  9.  
  10. #ifdef AMIGA
  11. #define R_PORT    "(Amiga 0.02)"
  12. #endif
  13.  
  14. #ifdef OS_2
  15. #define R_PORT    "(OS/2 1.0)"
  16. #endif
  17.  
  18. #ifdef MSDOS
  19. #define R_PORT    "(MSDOS)"
  20. #endif
  21.  
  22. /*
  23. -----------------------------------------------------------------------------
  24.     1 January 1991: Will now accept quoted strings and hex numbers in the
  25.     SIGNATURE and ARC lines.
  26.      - A whitespace character is a space or tab.
  27.      - Normally, each non-whitespace character is simply treated as a
  28.        character.
  29.      - If you place double-quotes (") around a string or part of a string, 
  30.        that string will be accepted intact, including whitespace.
  31.      - If you place a less-than sign (<) in a string, the following characters
  32.        will be considered to be 2-digit hex numbers until a greater-than sign
  33.        (>) is reached. A hex digit is any numeric digit (0-9) or any letter 
  34.        from A to F (a-f or A-F). Normally the hex digits will be taken in
  35.        pairs, but if any other character is imbedded, it will be considered to
  36.        be a number break and the next digit will start the next number.
  37.      - If you imbed a backslash ('\'), the backslash will be thrown away and
  38.        the next character will be accepted as a character. This allows you
  39.        to imbed the double-quote (") and less-than (<) characters as
  40.        characters.
  41.     10 December 1990: Now sorts .ARC signatures, primarily by ARC version, 
  42.     secondarily by signature level. This means the user need not worry about
  43.     the order, and it simplifies explanation greatly. Added new examples for
  44.     PAK 2.51 to POLYXARC.CFG.
  45.     18 September 1990: Added support for type 1 ARC headers (see PAKSORTM).
  46.     Added support for version 6 headers; version 5 ARC programs can't handle 
  47.     them. There is now an alternate verb in the config file for version 6 
  48.     archivers, ARC6. ARC or ARC5 will work for the old stuff.
  49.     21 May, 1990: Added the -# flag, which tells PolyXarc to ignore any
  50.     bundle names that don't end with a decimal digit when using the -F
  51.     option. Occasionally a file with a .MOD or some such extension shows
  52.     up, and can cause all kinds of havoc. The default is to process them.
  53.     23 May, 1990: Added a visual indication of the errorlevel at exit to
  54.     expedite debugging.
  55. Version 2.0, 9 April 1990: Cleaned up some minor stuff. Lattice 'C' on the 
  56.     Amiga seems to have a problem with leading zeros with the %.3d format, so
  57.     we changed it to %0.3d for the Amiga implementation only. Ripped out a
  58.     bunch of comments that described the .CFG file in detail; this code is
  59.     big enough as it is! Released version 2.0 (finally!). jjn
  60.     31 March 1990: Bill Andrus added definitions, DIRFIND.C, and conditional
  61.     compilations for IBM C/2 1.1 (or MSC 5.1) under OS/2 with the IBM SDK
  62.     Version 1.2 Toolkit.  Also insured the MSC conditional works for C/2.
  63.     22 March 1990: Steve Palm added definitions and conditional compilations
  64.     for Lattice C on the Amiga. I altered some of the stuff to make it even
  65.     more portable.
  66.     20 February 1990: Changed the way the template works. First I eliminated
  67.     the format type. Then I changed the %s specifications to %1, %2, %3, and
  68.     %4. Then I added the ability to have more than one file spec on the
  69.     command line; everything after the first spec will name a file to be
  70.     extracted. So now the four template specs are:
  71.         %1: extract string
  72.         %2: overwrite string
  73.         %3: archive name string
  74.         %4: extract name string
  75. Version 1.1a, 3 December 1989: I found another flaw in the bubble sort
  76.     routine. jjn
  77. Version 1.1, 2 December 1989: David Page pointed out that SPAZ would
  78.     automatically assume a wildcard extension if the archive name was
  79.     specified without an extension. I have added this to PolyXarc. jjn
  80. Version 1.0, 2 December 1989: This is the initial release. I now have the
  81.     NOSORT verb override the -F switch if it is used. That is, specifying
  82.     -F and NOSORT will cause PolyXarc not to sort .ARC files. Also found a
  83.     logic error in my bubble sort and fixed it. jjn
  84. Version 0.7, 25 November 1989: Previously, any error would cause PolyXarc
  85.     to exit. Now, if it is in -f mode, if the file is 0-length then PolyXarc
  86.     will simply delete it; if it gets an error return from the archiver, it
  87.     spits out a warning message, renames the file to BAD_ARC.???, and keeps
  88.     going. I also changed the config file a bit. jjn
  89. Version 0.6, 21 November 1989: Whoops! this_arc didn't get reset after each
  90.     file, so if you unpacked an .ARC file and then something else, it
  91.     wouldn't work. Fixed that by resetting this_arc at the same time as
  92.     this_signature. Added wildcard support for the non-f mode, which should
  93.     have been there in the first place. jjn
  94. Version 0.5, 20 November 1989: Fixed a bug in the -m support; I was
  95.     subtracting in the wrong direction. If the configuration file is not
  96.     specified on the command line, PolyXarc now looks for the default in
  97.     the current directory, then in the directory of the .EXE file, then
  98.     looks for a PENGUIN file. jjn
  99. Version 0.4, 18 November 1989: Fixed a bug in the -f support: Turbo C cannot
  100.     malloc(0), so I now always malloc() at least one item. jjn
  101. Version 0.3, 18 November 1989: More code cleanup. Removed the extensive
  102.     help stuff. Changed from Microsoft C 5.1 to Turbo C 2.0, which went from
  103.     96k to 32k (runtime, not .EXE size). Added the runtime configuration
  104.     display and -f option support. jjn
  105. Version 0.2, 17 November 1989: Cleaned up some error handling. jjn
  106. Version 0.1, 16 November 1989: Created by Jeffrey J. Nonken
  107. -----------------------------------------------------------------------------
  108.  
  109. Public domain by Jeffrey J. Nonken, February 1990. No strings, no bullshit.
  110. Do what you want with it. Nobody owns it.
  111.  
  112. However, I would like it if you would refer all changes and enhancements to
  113. me. This way I can maintain it properly. Thanks.
  114.  
  115. Jeffrey J. Nonken: sysop of Opus 1:273/715 Blue Bell, Pa. (215)279-9799
  116. Please send correspondence to:
  117. 507 Ave. Presidio
  118. San Clemente, Ca. 92672
  119.  
  120. This was compiled with Turbo C v2.0.
  121.  
  122. -----
  123.  
  124. Amiga Correspondence can go to:
  125. Steven M. Palm, sysop of "The Siberian Amiga (MAIL ONLY) 1:11/16"
  126. 1000 E. Easterday Ave, #2
  127. Sault Ste. Marie, MI 49783
  128.  
  129. All thanks and credit for the program belong to Jeffrey, though.
  130.  
  131. Amiga version compiled under Lattice C 5.05
  132.  
  133. -----
  134.  
  135. Bill Andrus of 1:109/301 is responsible for the OS/2 port.
  136.  
  137. -----
  138.  
  139. Please note: this source should compile under Lattice C 5.05 on the Amiga,
  140. or Turbo C 2.0 or Microsoft C 5.1 under MSDOS on the IBM. All you need do is
  141. predefine the appropriate label on the command line. However, Turbo C has
  142. a problem with the source files if they have been formatted by the Amiga.
  143. TC requires CRLF newline sequences, where the Amiga only uses LF for newlines.
  144. MSC doesn't seem to have any problem with the Amiga's newline sequences.
  145.  
  146. It should also compile under C/2 under OS/2.
  147.  */
  148.  
  149. #include <stdio.h>
  150. #include <stdlib.h>
  151. #include <string.h>
  152. #include <ctype.h>
  153. #include <dos.h>
  154. #include <errno.h>
  155.  
  156. #ifdef AMIGA
  157. #define _NEAR_
  158. #endif
  159.  
  160. #ifdef TURBOC
  161. #include <dir.h>
  162. #include <process.h>
  163. #define _NEAR_
  164. #endif
  165.  
  166. #ifdef MSC
  167. #include <malloc.h>
  168. #include <memory.h>
  169. #include <process.h>
  170. #define _NEAR_ near
  171. #endif
  172.  
  173. #ifdef OS_2
  174. #include <process.h>
  175. extern int dir_findfirst(char *filename,int attribute,struct find_t *dta);
  176. extern int dir_findnext(struct find_t *dta);
  177. #endif
  178.  
  179. #include "p_common.h"       /* Portability defines.  Must be after */
  180.                             /* system includes on the Amiga... */
  181.  
  182. #include "pakmdefn.h"       /* Mike Housky's sort function definitions. */
  183.  
  184. /****************************************************************************/
  185.  
  186. #ifdef TURBOC
  187. #define bf_name ff_name
  188. #define bf_time ff_ftime
  189. #define bf_date ff_fdate
  190. #define bf_size ff_fsize
  191. #endif
  192. #ifdef AMIGA
  193. #define bf_name filename
  194. #define bf_time time
  195. #define bf_date date
  196. #define bf_size size
  197. #endif
  198. #ifdef MSC
  199. #define bf_name name
  200. #define bf_time wr_time
  201. #define bf_date wr_date
  202. #define bf_size size
  203. #endif
  204.  
  205. #define OVER_LEN 6                    /* Size of overwrite string. */
  206. #define EXTR_LEN 6                    /* Size of extract string. */
  207. #define SIGN_LEN 6                    /* Size of signature string. */
  208.  
  209. #define DEFAULT_NAME "POLYXARC.CFG" /* Default name for configuration file. */
  210.  
  211. typedef struct SIGNATURE_TAG        /* 'Signature' structure. */
  212. {
  213.     long    file_offset;            /* Offset from beginning/end of file. */
  214.     char    overwrite[OVER_LEN];    /* Overwrite parameter string. */
  215.     char    extract[EXTR_LEN];        /* Extract parameter string. */
  216.     char    signature[SIGN_LEN];    /* Actual signature string. */
  217.     char    *cmdline;                /* Command line for calling de-archiver. */
  218. } SIGNATURE_TYPE;
  219.  
  220. typedef struct ARC_TAG                /* 'Archive level' structure. */
  221. {
  222.     byte    type;                    /* .ARC level */
  223.     byte    version;                /* True if version 6 headers found. */
  224.     char    overwrite[OVER_LEN];    /* Overwrite parameter string. */
  225.     char    extract[EXTR_LEN];        /* Extract parameter string. */
  226.     char    *cmdline;                /* Command line for calling de-archiver. */
  227. } ARC_TYPE;
  228.  
  229. typedef struct FILE_TAG             /* Bundle file directory information. */
  230. {
  231. #ifdef AMIGA
  232.     char          filename[108];    /* currently 30 chars are used */
  233.     long          time;
  234.     long          date;
  235. #endif
  236. #ifdef IBM
  237.     char          filename[13];     /* FILENAME.EXT of file. */
  238.     unsigned      time;             /* Last time updated. */
  239.     unsigned      date;             /* Last date updated. */
  240. #endif
  241.     long          size;             /* Size of the file in bytes. */
  242. } FILE_TYPE;
  243.  
  244. /****************************************************************************/
  245.  
  246. FILE_TYPE    * _NEAR_ file_list = NULL;    /* list of files ('malloc'ed). */
  247. ARC_TYPE    * _NEAR_ arc_list;
  248. SIGNATURE_TYPE * _NEAR_ signature_list;
  249.  
  250. int _NEAR_ this_net;                /* Our net number. */
  251. int _NEAR_ this_node;                /* Our node number. */
  252. int _NEAR_ config_name_set = 0;     /* TRUE if config name specified. */
  253. int _NEAR_ archive_name_set = 0;    /* TRUE if archive name specified. */
  254. int _NEAR_ arc_count = 0;            /* Number of .ARC formats defined. */
  255. int _NEAR_ signature_count = 0;     /* Number of other formats defined. */
  256. int _NEAR_ nosort = 0;                /* TRUE if should not sort .ARC files. */
  257. int _NEAR_ delete_archives = 0;     /* TRUE if should auto-delete files. */
  258. int _NEAR_ do_all_bundles = 0;        /* TRUE if processing all mail bundles. */
  259. int _NEAR_ do_n_bundles = 0;        /* Number of archives to do (0 == all). */
  260. int _NEAR_ show_addresses = 0;        /* TRUE to display bundle addresses. */
  261. int _NEAR_ this_signature = -1;     /* Which signature was found. */
  262. int _NEAR_ this_arc = -1;            /* Which arc level was found. */
  263. int _NEAR_ arc = 0;                    /* TRUE if an .ARC format file found. */
  264. int _NEAR_ overwrite = 0;            /* TRUE if automatic overwrite. */
  265. int _NEAR_ quiet = 0;                /* TRUE makes PolyXarc quieter. */
  266. int _NEAR_ name_count = 0;            /* Counts number of non-switch parameters. */
  267. int _NEAR_ ignore_archiver_errors = 0;    /* TRUE continues on archiver errors. */
  268. int _NEAR_ digits_only = 0;            /* TRUE forces digits only on bundle names. */
  269.  
  270.  
  271. #ifdef AMIGA
  272.     extern    int errno;
  273.     int     _NEAR_ msflag    = 1;            /* causes dir searches to use */
  274.                                         /* MS-DOS wildcards */
  275. #endif
  276. char _NEAR_ archive_name[fname_len] = "";/* Name of archive (from command line). */
  277. char _NEAR_ extract_name[128] = "";     /* Name(s) of file(s) to be extracted
  278.                                                         (from command line). */
  279. char _NEAR_ config_name[fname_len] = DEFAULT_NAME;    /* Name of configuration file. */
  280.  
  281. /****************************************************************************
  282. The following define the find_first/find_next environment. The functions that
  283. come with MSC and TURBO C are nearly identical, varying only in some minor
  284. details. 'buffer' is the structure in which the file search routines store
  285. the file information. 'jjn_findfirst' and 'jjn_findnext' are macros that
  286. resolve to the actual calls to the first/next library routines in the two
  287. different libraries. The only differences are the function names, and that two
  288. parameters are swapped in the find_first routine.
  289. ****************************************************************************/
  290. #ifdef TURBOC
  291. static struct ffblk _NEAR_ buffer;
  292. #define jjn_findfirst(f,a,b) findfirst(f,b,a)
  293. #define jjn_findnext(b) findnext(b)
  294. #endif
  295.  
  296. #ifdef AMIGA
  297. struct ffblk {
  298.         LONG    diskkey;
  299.         LONG    direntrytype;
  300.         char    filename[108];
  301.         LONG    protection;
  302.         LONG    entrytype;
  303.         LONG    size;
  304.         LONG    numblocks;
  305.         LONG    date;
  306.         LONG    time;
  307.         LONG    ticks;
  308.         char    comment[80];
  309.         char    reserved[36];
  310.     };
  311. static struct ffblk _NEAR_ buffer;
  312. #    define jjn_findfirst(f,a,b) dfind((struct FileInfoBlock *) b,(char *) f,(int) a)
  313. #    define jjn_findnext(b) dnext((struct FileInfoBlock *) b)
  314. #endif
  315.  
  316.  
  317. #ifdef MSC
  318.  
  319. #ifdef MSDOS
  320. static struct find_t _NEAR_ buffer;
  321. #define jjn_findfirst(f,a,b) _dos_findfirst(f,a,b)
  322. #define jjn_findnext(b) _dos_findnext(b)
  323. #endif
  324.  
  325. #ifdef OS_2
  326. static struct find_t _NEAR_ buffer;
  327. #define jjn_findfirst(f,a,b) dir_findfirst(f,a,b)
  328. #define jjn_findnext(b) dir_findnext(b)
  329. #endif
  330.  
  331. #endif
  332.  
  333. /* This holds the highest compression level found in an .ARC file. */
  334. extern int16 _NEAR_ highest_type;
  335. /* This tells whether we have found version 6 headers. */
  336. extern int16 _NEAR_ version6;
  337.  
  338.  
  339. /*****************************************************************************
  340.       quit() tells what errorlevel is being returned, then calls exit().
  341. *****************************************************************************/
  342. void quit(int errorlevel)
  343. {
  344.     if (!quiet) 
  345.         printf("\nErrorlevel on exit: %d.\n",errorlevel);
  346.     exit(errorlevel);
  347. }
  348.  
  349. /*****************************************************************************
  350. default_ext() appends a default extension to a filename. Normal operation
  351. happens when the given default extension does not have a period as the first
  352. character. If this is the case, the default extension is only appended if
  353. dest does not already have one. If the given default extension has a period,
  354. it forces the dest to have the default extension even if it already had one.
  355.  
  356.  -----> NOTE:  The AmigaDOS system does not require nor expect a filename
  357.                to contain an extension.  DO NOT USE THIS ROUTINE there.
  358.  
  359. Examples:
  360.   Assume filename is "MYFILE.JNK".
  361.  
  362.   default_ext(filename,"EXT");  [filename is left as "MYFILE.JNK".]
  363.   default_ext(filename,".EXT"); [filename is now "MYFILE.EXT".]
  364.  
  365.   Assume filename is "MYFILE".
  366.  
  367.   default_ext(filename,"EXT");  [filename is now "MYFILE.EXT".]
  368.   default_ext(filename,".EXT"); [filename is now "MYFILE.EXT".]
  369. *****************************************************************************/
  370. #ifndef AMIGA
  371. char *default_ext(char *dest,char *def_ext)
  372. {
  373. register int i;
  374.  
  375.     for (i = strlen(dest) - 1; i >= 0; --i) /* Look for \, :, or . */
  376.     {
  377.         if (dest[i] == '.')         /* Found a period. Force it? */
  378.         {
  379.             if (def_ext[0] != '.')  /* If none in default ext, forget it. */
  380.             {
  381.                 return(dest);        /* Return the given extension. */
  382.             }
  383.             else
  384.             {
  385.                 strcpy(dest+i,def_ext); /* Otherwise, copy new default ext. */
  386.                 return(dest);            /* Return with a new extension. */
  387.             }
  388.         }
  389.         if (dest[i] == c_slash || dest[i] == ':')   /* Found one, no period. */
  390.             i = 0;                                /* Force it to drop through. */
  391.     }
  392.     /* If it got this far, there was no extension. */
  393.     if (def_ext[0] != '.')          /* Is there a period in the default ext? */
  394.         strcat(dest,".");           /* If not, add one. */
  395.     strcat(dest,def_ext);            /* Now add the default extension. */
  396.     return(dest);
  397. }
  398. #endif
  399.  
  400. /****************************************************************************
  401. Limit a string to len characters. This routine modifies the original string
  402. and returns the string's address.
  403. ****************************************************************************/
  404. char *strtrunc(char *string,int len)
  405. {
  406. register int i;                 /* Index into the string. */
  407.  
  408.     i = 0;                        /* Start from the beginning. */
  409.     while (string[i] != '\0')   /* Do until we reach the end. */
  410.     {
  411.         if (i == len)            /* Did we count len characters? */
  412.         {
  413.             string[i] = '\0';   /* Yes, truncate the string here. */
  414.             return(string);     /* Return the truncated string. */
  415.         }
  416.         ++i;                    /* Not found yet, look farther. */
  417.     }
  418.     return(string); /* String too short for truncation, return unmodified. */
  419. }
  420.  
  421. /****************************************************************************
  422. Add a backslash (\) to the end of a path. Only does so if the last character
  423. is not already a backslash or a colon. Note: the Amiga uses a forward slash.
  424. ****************************************************************************/
  425. #ifdef IBM
  426. #    pragma check_stack-
  427. #endif
  428. void slash(char *pathspec)
  429. {
  430. register int        i;
  431.  
  432.     i = strlen(pathspec);                /* Find the end of the string. */
  433.     if (i--)    /* Check for 0 length. If non-zero, backpedal and process it. */
  434.         if ((pathspec[i] != c_slash) && /* See if last char is a backslash */
  435.                 (pathspec[i] != ':'))   /*  or a colon. */
  436.             strcat(pathspec,s_slash);    /* If not, add a backslash. */
  437. }
  438. #ifdef IBM
  439. #    pragma check_stack+
  440. #endif
  441. /****************************************************************************
  442. Separate drive:directory from name.ext. The original string is not changed.
  443. ****************************************************************************/
  444. void path_name(char *filespec,char *path,char *name)
  445. {
  446. register int    i;
  447.  
  448.     strupr(filespec);                            /* Make it upper case. */
  449.     for (i = strlen(filespec); i >= 0; --i)     /* Start from the end. */
  450.     {
  451.         if (filespec[i] == ':' || filespec[i] == c_slash)   /* '\' or ':'? */
  452.         {
  453.             ++i;                /* Yes, point to next char. */
  454.             if (name != NULL)    /* Copy filename if there is an address. */
  455.                 strcpy(name,filespec + i);
  456.             if (path != NULL)    /* Copy file if there is an address. */
  457.             {
  458.                 strncpy(path,filespec,i);    /* Copy only i characters. */
  459.                 path[i] = '\0';             /* Terminate the dest string. */
  460.             }
  461.             return;             /* We got it; now let's return it. */
  462.         }
  463.     }
  464.  
  465. /* If we get this far, we never found a colon or backslash. That implies that
  466.    the filespec is filename.ext only, no path. */
  467.  
  468.     if (path != NULL)        /* Copy empty path if an address was provided. */
  469.         strcpy(path,"");
  470.     if (name != NULL)        /* Copy filename.ext if an address was provided. */
  471.         strcpy(name,filespec);
  472. }
  473.  
  474. /****************************************************************************
  475.            This converts a hexASCII digit into a numeric value.
  476. ****************************************************************************/
  477. char hexit(char c)
  478. {
  479.     toupper(c);
  480.     c -= '0';
  481.     if (c > 9)
  482.         c -= 7;
  483.     return(c);
  484. }
  485.  
  486. /****************************************************************************
  487. This parses off one parameter from the given string and returns its address.
  488. It works similarly to strtok() except that only the string parameter is 
  489. specified; the parsing action is fixed. 
  490.  
  491. On the first call per string, pass the string address.
  492. On subsequent calls for that string, pass a NULL pointer.
  493. ****************************************************************************/
  494. #define SCANNING 1
  495. #define QUOTE    2
  496. #define HEX1     3
  497. #define HEX2     4
  498. #define FINISHED 5
  499. #define DEADSTOP 6
  500.  
  501. char *parmtok(char *string,char *tokens)
  502. {
  503. static char *oldstring;
  504. char *newstring;
  505. register int i;
  506. register int j;
  507. register int state;
  508.  
  509.     if (string != NULL)
  510.         oldstring = string;
  511.     while (strchr(tokens,*oldstring) != NULL)
  512.         if (*oldstring == '\0')
  513.             return(NULL);
  514.         else
  515.             ++oldstring;
  516.     i = 0;
  517.     j = 0;
  518.     state = SCANNING;
  519.     do
  520.     {
  521.         switch(state)
  522.         {
  523.             case SCANNING:
  524.                 if (oldstring[i] == '\0')
  525.                     state = DEADSTOP;
  526.                 else if (strchr(tokens,oldstring[i]) != NULL)
  527.                     state = FINISHED;
  528.                   else if (oldstring[i] == '"')
  529.                       state = QUOTE;
  530.                   else if (oldstring[i] == '\\')
  531.                   {
  532.                       if (oldstring[++i] == '\0')
  533.                         state = DEADSTOP;
  534.                       else
  535.                           oldstring[j++] = oldstring[i];
  536.                   }
  537.                   else if (oldstring[i] == '<')
  538.                       state = HEX1;
  539.                   else 
  540.                       oldstring[j++] = oldstring[i];
  541.                   break;
  542.               case QUOTE:
  543.                   if (oldstring[i] == '\\')
  544.                       oldstring[j++] = oldstring[++i];
  545.                   else if (oldstring[i] == '"')
  546.                       state = SCANNING;
  547.                   else if (oldstring[i] == '\0')
  548.                     state = DEADSTOP;
  549.                 else
  550.                       oldstring[j++] = oldstring[i];
  551.                   break;
  552.               case HEX1:
  553.               case HEX2:
  554.                 if (oldstring[i] == '>')
  555.                     state = SCANNING;
  556.                 else if (oldstring[i] == '\0')
  557.                     state = DEADSTOP;
  558.                 else if (isxdigit(oldstring[i]))
  559.                 {
  560.                     if (state == HEX1)
  561.                     {
  562.                         oldstring[j] = hexit(oldstring[i]);
  563.                         state = HEX2;
  564.                     }
  565.                     else
  566.                     {
  567.                         oldstring[j] = (oldstring[j] << 4) + hexit(oldstring[i]);
  568.                         state = HEX1;
  569.                         ++j;
  570.                     }
  571.                 }
  572.                 else
  573.                 {
  574.                     if (state == HEX2)
  575.                         ++j;
  576.                     state = HEX1;
  577.                 }
  578.                 break;
  579.         }
  580.         ++i;
  581.     } while (state != FINISHED && state != DEADSTOP);
  582.     oldstring[j] = '\0';
  583.     newstring = oldstring;
  584.     oldstring += (state == DEADSTOP) ? (i - 1) : i;
  585.     return(newstring);
  586. }
  587.  
  588. /****************************************************************************
  589.      If the first file timestamp is later than the second, return TRUE.
  590. ****************************************************************************/
  591. #ifdef IBM
  592. #    pragma check_stack-
  593. #endif
  594. int compare(FILE_TYPE *f1,FILE_TYPE *f2)
  595. {
  596.     if (f1->date > f2->date)    /* If first file date is later, return TRUE. */
  597.         return(1);
  598.     if (f1->date < f2->date)    /* If first file date is earlier, FALSE. */
  599.         return(0);
  600. /* The dates were the same, so try the time. */
  601.     if (f1->time > f2->time)    /* If first file time is later, return TRUE. */
  602.         return(1);
  603. /* The first file is the same as or earlier than the second. In either case,
  604.    we don't want to bother swapping, so just return FALSE. */
  605.     return(0);
  606. }
  607. #ifdef IBM
  608. #    pragma check_stack+
  609. #endif
  610. /****************************************************************************
  611.       Find highest BAD_ARC.???, rename current file to the next one.
  612. ****************************************************************************/
  613. int ren_bad_arc(char *path,char *bad_name)
  614. {
  615. register int    i;                /* Highest BAD_ARC.### found so far. */
  616. register int    j;                /* Holds results from first/next calls. */
  617. register int    k;                /* Converted BAD_ARC.### value. */
  618. char            filespec[fname_len];    /* Filespec of new name. */
  619.                                         /* Also first/next search pattern. */
  620. char            bad_spec[fname_len];    /* Complete filespec of bad filename. */
  621.  
  622.     sprintf(filespec,"%sBAD_ARC.???",path);     /* Create search pattern. */
  623.     i = -1;                                     /* Highest found so far. */
  624.     j = jjn_findfirst(filespec, 0, &buffer);    /* Find the first one. */
  625.     while (j == 0)                            /* While we keep finding them... */
  626.     {
  627.         /* Convert the extension to its numeric value. */
  628.         k = atoi(strchr(buffer.bf_name,'.') + 1);
  629.         if (k > i)            /* Is it higher than all the previous found? */
  630.             i = k;            /* If so, save this one as the highest. */
  631.         j = jjn_findnext(&buffer);    /* Find the next one, if you can! */
  632.     }
  633. /* Gets here when no more can be found. */
  634. /* Create a filespec including the path and BAD_ARC.(i+1). */
  635. #ifdef AMIGA
  636.     sprintf(filespec,"%sBAD_ARC.%0.3d",path,i + 1);
  637. #else
  638.     sprintf(filespec,"%sBAD_ARC.%.3d",path,i + 1);
  639. #endif
  640. /* Create a filespec including the path and the original filename. */
  641.     sprintf(bad_spec,"%s%s",path,bad_name);
  642. /* Tell the operator what is happening. */
  643.     printf("Renaming %s to %s.\n",bad_spec,filespec);
  644. /* Rename the file to BAD_ARC.###. */
  645.     rename(bad_spec,filespec);
  646.     return(i);
  647. }
  648.  
  649. /****************************************************************************
  650.        Find all .SU?, .MO?, .TU?, .WE?, .TH?, .FR?, and .SA? files.
  651. ****************************************************************************/
  652. int get_files(char *path,int do_bundles)
  653. {
  654. char            *p;
  655. register int    i;
  656. register int    j;
  657. register int    k;
  658. int             found;
  659. int                ok;
  660. char            filespec[64];
  661. static char bundles[7][6] =
  662.     {
  663.         "*.SU?",
  664.         "*.MO?",
  665.         "*.TU?",
  666.         "*.WE?",
  667.         "*.TH?",
  668.         "*.FR?",
  669.         "*.SA?"
  670.     };
  671. FILE_TYPE        f;
  672.  
  673.  
  674.     strcpy(filespec,path);        /* Start the filespec out with the path. */
  675.     p = filespec + strlen(filespec);    /* Point to the end of the path.
  676.                                            This lets us use strcpy() instead
  677.                                            of strcat to concatonate, and
  678.                                            relieves us from having to copy the
  679.                                            path back in every time. */
  680.     file_list = (FILE_TYPE *)malloc(sizeof(FILE_TYPE)); /* Allocate 1. */
  681.     if (file_list == NULL)                /* Check to see if it worked. */
  682.         return(-1);                     /* If not, we have a memory problem. */
  683.     i = 0;                                /* Start index/counter at 0. */
  684.     for (j = 0; j < 7; ++j)             /* Run through all the extensions. */
  685.     {
  686.         if (do_bundles)                 /* If doing all bundles, */
  687.             strcpy(p,bundles[j]);        /*    get the search pattern. */
  688.         else                /* Otherwise we're just looking for one file; */
  689.             j = 7;            /*    set index for once through. */
  690.         if (jjn_findfirst(filespec, 0, &buffer) == 0)    /* Find first one. */
  691.         {
  692.             ok = TRUE;
  693.             if (digits_only)
  694.                 if (!isdigit(buffer.bf_name[strlen(buffer.bf_name) - 1]))
  695.                     ok = FALSE;
  696.             if (ok)
  697.             {                    /* If we found one, allocate room for more. */
  698.                 file_list = (FILE_TYPE *)realloc((char *)file_list,(i + 1) * sizeof(FILE_TYPE));
  699.                 if (file_list == NULL)
  700.                     return(-1); /* If allocation didn't work, no more memory. */
  701.                 strcpy(file_list[i].filename,buffer.bf_name);    /* Get filename. */
  702.                 file_list[i].time = buffer.bf_time;                /* Get time. */
  703.                 file_list[i].date = buffer.bf_date;                /* Get date. */
  704.                 file_list[i].size = buffer.bf_size;                /* Get size. */
  705.                 ++i;                                            /* Count up. */
  706.             }
  707.             while (jjn_findnext(&buffer) == 0)    /* We're greedy, look for more. */
  708.             {
  709.                 ok = TRUE;
  710.                 if (digits_only)
  711.                     if (!isdigit(buffer.bf_name[strlen(buffer.bf_name) - 1]))
  712.                         ok = FALSE;
  713.                 if (ok)
  714.                 {                /* If we found one, allocate room for more. */
  715.                     file_list = (FILE_TYPE *)realloc((char *)file_list,(i + 1) * sizeof(FILE_TYPE));
  716.                     if (file_list == NULL)
  717.                         return(-1); /* If allocation didn't work, no more memory. */
  718.                     strcpy(file_list[i].filename,buffer.bf_name);/* Get filename. */
  719.                     file_list[i].time = buffer.bf_time;             /* Get time. */
  720.                     file_list[i].date = buffer.bf_date;             /* Get date. */
  721.                     file_list[i].size = buffer.bf_size;             /* Get size. */
  722.                     ++i;                                         /* Count up. */
  723.                 }
  724.             }
  725.         }
  726.     }
  727. /* We've found all the filenames there are to find. Now we use a bubble sort
  728.    to get them all into order by time/datestamp. I used a bubble sort because
  729.    the quicksort is much larger, and besides, I don't expect there to be
  730.    enough files to make the quicksort time-effective. In fact, I expect that
  731.    the files will usually be in order, in which case the bubble sort is
  732.    faster because it will only require one pass to find that out. */
  733.  
  734.     if (i > 1)                        /* Did we find more than one file? */
  735.     {                                /* Yes, do a bubble sort. */
  736.         j = 0;                        /* Start outside index at 0. */
  737.         do
  738.         {
  739.             found = FALSE;            /* This flag says whether any swaps made. */
  740.  
  741. /* The inside loop starts from the top and goes down to the outside loop
  742.    index. That way the lowest gets sorted to the bottom each time, and we
  743.    can safely ignore it from then on, and so increment the outside counter. */
  744.  
  745.             for (k = i - 2; k >= j; --k)
  746.             {
  747. /* We want the earlier file first. Compare returns TRUE if the earlier one
  748.    is second. If they are the same date, we don't care and leave them alone. */
  749.                 if (compare(&file_list[k],&file_list[k+1]))
  750.                 {                    /* Wrong order, swap the files. */
  751.                     found = TRUE;    /* Trip the flag; forces another pass. */
  752.                     memcpy((char *)&f,(char *)&file_list[k],sizeof(f));
  753.                     memcpy((char *)&file_list[k],(char *)&file_list[k+1],sizeof(f));
  754.                     memcpy((char *)&file_list[k+1],(char *)&f,sizeof(f));
  755.                 }
  756.             }
  757.         } while (++j < (i - 1) && found);    /* Do until they're in order. */
  758.     }
  759.     return(i);                    /* Return the number of files found. */
  760. }
  761.  
  762. /************************************************************************
  763.         Build the command line, based upon template & cmd type
  764.  ***********************************************************************/
  765. int build_cmd (char *extract_spec, char *overwrite_s, char *extract_s,
  766.                     char *template_s, char *filespec, char *cmdline)
  767. {
  768. int i, j, percent;
  769. char c, *t;
  770.  
  771. /* Build the command line according to the template. */
  772.  
  773.     for (i = 0, j = 0, percent = FALSE; template_s[i] != '\0'; ++i)
  774.     {
  775.         c = template_s[i];            /* Get the character. */
  776.         if (percent)                /* Was the last one a '%'? */
  777.         {
  778.             t = NULL;                /* Yup. Init this to NULL for testing. */
  779.             switch(c)                /* Find out which string we want. */
  780.             {
  781.               case '1': t = overwrite ? "" : extract_s;
  782.                         break;            /* Get the extract string. */
  783.               case '2': t = overwrite ? overwrite_s : "";
  784.                         break;            /* Get the overwrite string. */
  785.               case '3': t = filespec;
  786.                         break;            /* Get the file spec. */
  787.               case '4': t = extract_spec;
  788.                         break;            /* Get the extracted file spec. */
  789.               default:    cmdline[j++] = c;
  790.                         break;            /* Neither, just copy the character. */
  791.             }
  792.             if (t != NULL)                    /* If non-null, we got a string. */
  793.             {
  794.                 strcpy(cmdline + j,t);        /* Yup, copy the string in place of the %?. */
  795.                 j = strlen(cmdline);        /* Update the index. */
  796.             }
  797.             percent = FALSE;                /* Reset our '%' flag. */
  798.         }
  799.         else if (c == '%')              /* See if this is a '%'. */
  800.             percent = TRUE;             /* If so, process next character. */
  801.         else
  802.             cmdline[j++] = c;            /* Else just copy this character. */
  803.     }
  804.     cmdline[j] = '\0';                  /* Append a null. It's now a string. */
  805.     return (0);
  806. }
  807.  
  808. /************************************************************************
  809.                      Tokenize the command line.
  810. ************************************************************************/
  811. int tokenize (char *cmdline, int *argc, char *argv[], char **path)
  812. {
  813.     char *t;
  814.  
  815. /* Now we tokenize the command line. Separate everything at the spaces. I
  816.    currently ignore quotes, so this may want some enhancement. */
  817.     t = strtok(cmdline," ");
  818.     if (t == NULL)
  819.     {
  820.         printf("Error: encountered empty command line while attempting to tokenize.\n");
  821.         return(1);            /* No command found! Return an error. */
  822.     }
  823.     *path = t;                /* Store this as the execution path, */
  824.     argv[0] = t;            /*    and as the first argument. */
  825.     *argc = 1;                /* Set the argument counter/index. */
  826.     do
  827.     {
  828.         t = strtok(NULL," ");   /* Get the next token. */
  829.         argv[(*argc)++] = t;    /* Set it as the next argument and count up. */
  830.     } while (t != NULL);        /* Do it until we run out of tokens. */
  831. /* The beauty of that scheme is that the argument array is terminated by a
  832.    NULL address, which that loop will store as the last argument. */
  833.  
  834.     return(0);
  835. }
  836.  
  837. /****************************************************************************
  838. Create and execute a command. Don't use COMMAND.COM, we need the result code!
  839. ****************************************************************************/
  840. int execute(char *overwrite_s, char *extract_s, char *template_s,
  841.                                         char *filespec, char *extract_spec)
  842. {
  843. char    *argv[21];    /* Argument list. Allow 20 arguments + command name. */
  844. char    *path;            /* Command name. Also stored as argv[0]. */
  845. int     argc;            /* Number of arguments found. */
  846. int     i;                /* String index, and result of the spawn. */
  847. char    *t;             /* String pointer, and token pointer. */
  848. int     retval;
  849.  
  850. #ifdef AMIGA
  851. int     offset;         /* offset to just past the archiver name in cmd */
  852. char    justpath[110];
  853. char    justfile[110];
  854. char    buffer[256];    /* buffer for my new CMD string */
  855. char    cmdline[256];    /* The command string */
  856. struct ProcID pid;
  857. #else
  858. char    cmdline[128];    /* The command string. */
  859. #endif
  860.  
  861.     retval = build_cmd (extract_spec, overwrite_s, extract_s, template_s,
  862.                                    filespec, cmdline);
  863.     if (!quiet)
  864.         printf("-> Executing '%s'\n",cmdline);
  865.     if (retval != 0) return (retval);
  866.     retval = tokenize (cmdline, &argc, argv, &path);
  867.     if (retval != 0) return (retval);
  868.  
  869. #ifdef AMIGA
  870.     /*    A Bug in lattice causes the forkv function to NOT search */
  871.     /*    the C: device for commands.  This is why I have to manually */
  872.     /*    try that combination.  Darn irritating! */
  873.  
  874.     i = forkv(path,argv,NULL,&pid);  /* Spawn the task. */
  875.     if (i == -1) {
  876.         path_name (path, justpath, justfile);        /* get archiver name */
  877.         offset = strlen (path);       /* how far past name into cmdline? */
  878.         strcpy (buffer, "C:");                     /* start with C: path */
  879.         strcat (buffer, justfile);             /* and add the name on here */
  880.         retval = build_cmd (extract_spec, overwrite_s, extract_s, template_s,
  881.                             filespec, cmdline);
  882.         if (retval != 0) return (retval);
  883.         strcat (buffer, &cmdline[offset]);             /* move the args in */
  884.         strcpy (cmdline, buffer);  /* now put this all back into cmdline */
  885.         retval = tokenize (cmdline, &argc, argv, &path);
  886.         if (retval != 0) return(retval);
  887.         i = forkv (path, argv, NULL, &pid);
  888.     }
  889. #else
  890.     i = spawnvp(P_WAIT,path,argv);    /* Spawn the task. */
  891. #endif
  892.     if (i == -1)                /* Did we get an error trying to execute? */
  893.     {
  894.         switch (errno)            /* Yup, figure out why. */
  895.         {
  896.             case E2BIG:
  897.                 t = "Argument list too long";
  898.                 break;
  899.             case EINVAL:
  900.                 t = "Invalid argument";
  901.                 break;
  902.             case ENOENT:
  903.                 t = "Path or file name too long";
  904.                 break;
  905.             case ENOEXEC:
  906.                 t = "Exec format error";
  907.                 break;
  908.             case ENOMEM:
  909.                 t = "Not enough memory";
  910.                 break;
  911.  
  912.         /* we must put a default in, as Lattice is not properly
  913.            setting the errno flag!    Thus, I haven't tried to work
  914.            around it, I just admit it isn't specified... */
  915.         /* Actually, that's a good idea anyway. jjn */
  916.             default:
  917.                 t = "Unspecified Error";
  918.                 break;
  919.         }
  920.         printf("Error in execution; reason given: %s.\n",t);
  921.         return(-1);
  922.     }
  923. #ifdef AMIGA
  924.     i = wait(&pid); /* wait for our child process to terminate before
  925.                            we continue our processing.    Ain't multi-tasking
  926.                            nice??? ;^) */
  927.                     /* Whaddya think P_WAIT is for in the spawn command? :) */
  928. #endif
  929.     return(i);        /* No, we managed to execute it. Return the result. */
  930. }
  931.  
  932. /****************************************************************************
  933.       Search through the given file and figure out which type it is.
  934. ****************************************************************************/
  935. int find_type(char *path, char *filename, char *filespec)
  936. {
  937. FILE            *file;    /* File pointer. */
  938. SIGNATURE_TYPE    *s;     /* Pointer into signature list; provides dereferencing. */
  939. long            offset; /* Offset into the file. */
  940. register int    i;        /* Index into the signature list. */
  941. int             l;        /* Length of the signature. */
  942. char            buffer[SIGN_LEN];    /* Read buffer for the signatures. */
  943.  
  944.  
  945.     arc = FALSE;                    /* Note that no .ARC type found. */
  946.     file = fopen(filespec,"rb");    /* Open the file for binary read. */
  947.     if (file == NULL)                /* If can't open it, return with error. */
  948.     {
  949.         printf("Error: cannot open %s.\n",filespec);
  950.         return(1);
  951.     }
  952.     this_arc = -1;                    /* Show that nothing found so far. */
  953.     this_signature = -1;
  954.     i = 0;                            /* Start index at first signature. */
  955.     offset = 0xffffffff;            /* Show a ridiculous offset at first. */
  956.     while (i < signature_count && this_signature == -1)
  957.     {
  958.         s = &signature_list[i];     /* Dereference this signature. */
  959.         l = strlen(s->signature);    /* Get the signature length. */
  960.         if (s->file_offset != offset)    /* See if we already have that spot. */
  961.         {
  962.             offset = s->file_offset;    /* Nope, move to it and read it. */
  963.             fseek(file,offset,(offset < 0) ? 2 : 0);
  964.             fread(buffer,sizeof(buffer),1,file);
  965.         }
  966.         if (strncmp(s->signature,buffer,l) == 0)    /* Are they the same? */
  967.                                                     /* Note: case sensitive! */
  968.             this_signature = i;         /* If so, record it. */
  969.         ++i;                            /* Increment the counter. */
  970.     }    /* Do that until we find a signature or run out. */
  971.     if (this_signature == -1)            /* Did we find a signature? */
  972.     {                                    /* If not, look for an .ARC file. */
  973.         if (offset != 0l)                /* See if we have this in memory. */
  974.         {
  975.             fseek(file,0l,0);            /* If not, seek 0 and read 1 byte. */
  976.             fread(buffer,1,1,file);
  977.         }
  978.         if (buffer[0] == 0x1a)            /* 1A is the .ARC signature. */
  979.             arc = TRUE;                 /* If so, assume it's an .ARC file. */
  980.     }
  981.     fclose(file);                        /* Either way, we're done for now. */
  982.     if (arc)                            /* Is it an .ARC file? */
  983.     {
  984.         i = pak_dosort(path,filename,!nosort);    /* If so, try to sort. */
  985.         printf("Highest ARC level found: %d.\n",highest_type);
  986. /*
  987. Actually, pak_dosort() scans through the .ARC file, finding the highest
  988. level of compression used. It only sorts if nosort is FALSE.
  989.  
  990. Returns 0 if successful, or errorlevel for DOS on error:
  991.    1 = Can't open/read source. (missing or invalid pakfile)
  992.    2 = Can't create/write dest (maybe disk or directory full)
  993.    3 = Can't delete old source file. (read-only)
  994.    4 = Can't rename temp file to source name. (???)
  995.  
  996. If we got 2, print a warning and go on. Otherwise, we quit on any error.
  997. */
  998.         if (i == 2)
  999.         {
  1000.             printf("Warning: sort failed. Attempting extract anyway.\n");
  1001.         }
  1002.         else if (i != 0)
  1003.             return(i);
  1004. /* Did ok, so let's see what we've got. */
  1005.         i = 0;
  1006.         while (i < arc_count && this_arc == -1)
  1007.         {
  1008.             if ((version6 && arc_list[i].version) || !version6)
  1009.                 if (highest_type <= arc_list[i].type)
  1010.                     this_arc = i;    /* Set this_arc to .ARC type found. */
  1011. /* If the highest .ARC type is higher than we can handle, it never finds it.
  1012.    The calling routine will claim that we couldn't find the archive type.
  1013.    If the archive has version 6 headers, then the extractor must also be
  1014.    capable of version 6 extraction.  */
  1015.             ++i;
  1016.         }
  1017.     }
  1018.     return(0);
  1019. }
  1020.  
  1021. /****************************************************************************
  1022. Converts the first 4 characters from a string from hex to a signed integer.
  1023. If there are any non-digits in those four characters, it returns -65536.
  1024. ****************************************************************************/
  1025. long one_hex(char *name)
  1026. {
  1027. int                 i;        /* Index. */
  1028. register int16        j;        /* Accumulated result. */
  1029. register int16        c;        /* Holding place for the converted character. */
  1030.  
  1031.     j = 0;                        /* Reset accumulated result. */
  1032.     for (i = 0; i < 4; ++i)     /* Go through the first 4 characters. */
  1033.     {
  1034.         c = toupper(name[i]);    /* Get the upper case character. */
  1035.         if (!isxdigit(c))        /* Is it a hex digit? */
  1036.             return -65536;        /* If not, return error. */
  1037.         if (c >= 'A')           /* Change 'A'-'F' into A-F. */
  1038.             c -= 7;
  1039.         j = (j << 4) + c - '0'; /* Subtract ASCII offset and accumulate. */
  1040.     }
  1041.     return (long)j;             /* Return the result. */
  1042. }
  1043.  
  1044. /****************************************************************************
  1045.                          Display address of sender.
  1046. ****************************************************************************/
  1047. void sender_is(char *filename)
  1048. {
  1049. int             net;
  1050. int             node;
  1051. long            i;
  1052.  
  1053.     i = one_hex(filename);        /* Convert first 4 characters of filename. */
  1054.     if (i == -65536)            /* If not hex number, ignore it. */
  1055.         return;
  1056.     net = (int16)i;             /* Assume this is a net difference. */
  1057.     i = one_hex(filename + 4);    /* Convert last 4 characters of filename. */
  1058.     if (i == -65536)            /* If not hex number, ignore it. */
  1059.         return;
  1060.     node = (int16)i;            /* Assume this is a node difference. */
  1061.  
  1062. /* Calculate the net/node address from the filename and your address, and
  1063.    display the result. */
  1064.     printf("Extracting bundle from %u/%u.\n",this_net + net,this_node + node);
  1065. }
  1066.  
  1067. /****************************************************************************
  1068.                           Discombobulate one file.
  1069. ****************************************************************************/
  1070. int discombobulate(char *path,char *filename, char *extract_spec)
  1071. {
  1072. int     i;                /* General purpose variable. */
  1073. char    filespec[fname_len];    /* File specification generated from path and name. */
  1074.  
  1075.     if (show_addresses) /* If we're supposed to show the address, do so. */
  1076.         sender_is(filename);
  1077.     strcpy(filespec,path);        /* Create the filespec from the path */
  1078.     strcat(filespec,filename);    /*    and the filename. */
  1079.     i = find_type(path,filename,filespec);    /* Find the type of file. */
  1080.     if (i)                        /* If can't do it, return with the error. */
  1081.         return(i);
  1082.     if (this_arc != -1)         /* See if it's an .ARC file. */
  1083.     {
  1084.         i = execute(arc_list[this_arc].overwrite,
  1085.                     arc_list[this_arc].extract,
  1086.                     arc_list[this_arc].cmdline,
  1087.                     filespec,
  1088.                     extract_spec);    /* Try to extract. */
  1089.     }
  1090.     else if (this_signature != -1)    /* Nope, see if it's something else. */
  1091.     {
  1092.         i = execute(signature_list[this_signature].overwrite,
  1093.                     signature_list[this_signature].extract,
  1094.                     signature_list[this_signature].cmdline,
  1095.                     filespec,
  1096.                     extract_spec);    /* Try to extract. */
  1097.     }
  1098.     else                        /* Whoops! We couldn't figure it out. */
  1099.     {
  1100.         printf("Error: cannot determine archive type of %s.\n",filespec);
  1101.         if (arc && this_arc == -1)
  1102.             printf("The ARC level was higher than anything defined.\n");
  1103.         return(6);
  1104.     }
  1105.     if (i > 0)            /* This means the archiver ran into trouble. */
  1106.     {
  1107.         printf("Error return %d from archiver.\n",i);
  1108.         i = 8;
  1109.     }
  1110.     else if (i < 0)     /* This means we never got to execute it. */
  1111.     {
  1112.         return(10);
  1113.     }
  1114.     else if (delete_archives)    /* No problems. Erase the evidence. */
  1115.     {
  1116.         printf("Unpack successful: deleting %s.\n",filespec);
  1117.         i = unlink(filespec);
  1118.         if (i)
  1119.         {
  1120.             printf("Error: could not delete %s.",filespec);
  1121. #ifdef OS_2
  1122.             printf("\nERRNO is %d.",errno);
  1123. #endif
  1124.  
  1125.  
  1126.             i = 3;
  1127.         }
  1128.     }
  1129.     printf("\n");
  1130.     return(i);
  1131. }
  1132.  
  1133. /****************************************************************************
  1134.       Discombobulate all bundles (or n bundles if do_n_bundles != 0).
  1135. ****************************************************************************/
  1136. int discombobulate_all(char *path, char *extract_spec, int do_bundles)
  1137. {
  1138. int     numfiles;        /* How many files we found. */
  1139. int     count;            /* Index counter. */
  1140. int     i;                /* General purpose register. */
  1141.  
  1142.     numfiles = get_files(path,do_bundles);    /* Collect filenames. */
  1143.     if (numfiles < 0)        /*    < 0 means we got an error in allocation. */
  1144.         return(5);
  1145.  
  1146. /* Not having anything to discombobulate with the -F switch isn't _really_ an
  1147.    error condition. Having PolyXarc return an error under these circumstances 
  1148.    made me and Peter Stern spend several days beating our heads against the 
  1149.    wall. From now on we'll just return 0 if this happens. -jjn */
  1150.    
  1151.     if (numfiles == 0)        /* == 0 means no files found. */
  1152.         return(do_all_bundles ? 0 : 1);
  1153.     if (do_n_bundles)                        /* If limited, */
  1154.         if (numfiles > do_n_bundles)        /*    and if past the limit, */
  1155.             numfiles = do_n_bundles;        /*    limit the file count. */
  1156.  
  1157. /* If we're not doing bundles, then path was actually a filespec. In that case
  1158.    we need to throw away the filename.ext and keep the path. The filename.ext
  1159.    will be drawn from the list of files that we got from get_files(). */
  1160.     if (!do_bundles)
  1161.         path_name(path,path,NULL);
  1162.  
  1163.     for (count = 0; count < numfiles; ++count)    /* Do all the files. */
  1164.     {
  1165.         if (file_list[count].size == 0l)        /* Is this an empty file? */
  1166.         {
  1167.             if (do_bundles)                     /* Are we doing bundles? */
  1168.             {
  1169.                 unlink(file_list[count].filename);    /* Yes; just delete it. */
  1170.                 printf("Zero-length file %s deleted.",file_list[count].filename);
  1171.             }
  1172.             else                            /* No, consider this an error. */
  1173.             {
  1174.                 printf("Error: file %s is length 0.",file_list[count].filename);
  1175.                 return(9);
  1176.             }
  1177.         }
  1178.         else    /* The file is not zero length. */
  1179.         {
  1180.             i = discombobulate(path,file_list[count].filename,extract_spec);
  1181.                                     /* Extract. */
  1182.             if (do_bundles)         /* Are we doing bundles? */
  1183.             {
  1184.                 switch (i)            /* If so, look at the return value. */
  1185.                 {
  1186.                     case 0:         /* Nothing went wrong, do nothing. */
  1187.                         break;
  1188.                     case 1:
  1189.                     case 2:         /* Fatal error, return error code. */
  1190.                     case 4:
  1191.                     case 5:
  1192.                     case 7:
  1193.                         return(i);
  1194.                     default:        /* Non-fatal, rename file and continue. */
  1195.                         ren_bad_arc(path,file_list[count].filename);
  1196.                 }
  1197.             }
  1198.             else    /* Not doing bundles. If we got an error, just return it. */
  1199.             {
  1200.                 if (i == 8 && ignore_archiver_errors)
  1201.                     i = 0;
  1202.                 else
  1203.                     if (i)
  1204.                         return(i);
  1205.             }
  1206.         }
  1207.     }
  1208.     return(0);    /* No fatal errors. Return. */
  1209. }
  1210.  
  1211. /****************************************************************************
  1212.                      Find "PENGUIN=" in environment
  1213.  
  1214.  Unfortunately, under Amiga Lattice the 'getenv()' function only returns
  1215.  Commodore's Environment variables, *NOT* the more common Manx/ARP/Rockiki
  1216.  style variables...  I will try to remedy this in the future.
  1217. ****************************************************************************/
  1218. int p_env(void)
  1219. {
  1220. char    *t;
  1221.  
  1222.     t = getenv("PENGUIN");              /* Try to find a PENGUIN file. */
  1223.  
  1224.     if (t != NULL)                        /* See if "PENGUIN=" in environment. */
  1225.     {
  1226.         strcpy(config_name,strupr(t));    /* Get the name of the file. */
  1227.         return(1);                        /* Return TRUE. */
  1228.     }
  1229.     return(0);                            /* Nothing found, return FALSE. */
  1230. }
  1231.  
  1232. /****************************************************************************
  1233.                      Process the configuration file.
  1234.  
  1235. The configuration file consists of one or more blocks that start with
  1236. BEGIN POLYXARC and end with END POLYXARC (case insensitive). Between the
  1237. delimiting statements are one or more keywords, one per line. Most keywords
  1238. will have parameters sharing the line. Keywords are:
  1239.  
  1240. ARC level overwrite extract extract_command_template
  1241.  
  1242.     %1: extraction string
  1243.     %2: overwrite string
  1244.     %3: archive file spec
  1245.     %4: extracted file spec
  1246.  
  1247. SIGNATURE offset signature overwrite extract extract_command_template
  1248.  
  1249.     %1: extraction string
  1250.     %2: overwrite string
  1251.     %3: archive file spec
  1252.     %4: extracted file spec
  1253.  
  1254. NOSORT
  1255.  
  1256. ****************************************************************************/
  1257. int get_config(char *argv0)
  1258. {
  1259. FILE            *config;        /* Config file. */
  1260. char            *f;             /* Scratch string */
  1261. char            *g;             /* Scratch string */
  1262. char            *s1;            /* Pointer to signature token. */
  1263. char            *s2;            /* Pointer to overwrite token. */
  1264. char            *s3;            /* Pointer to extract token. */
  1265. char            oneline[132];    /* Holds one line from file. */
  1266. register int    i;                /* scratch integer */
  1267. int             quit;
  1268. int                arc6;            /* True if found version 6 definition. */
  1269. long            l;
  1270.  
  1271.     if (config_name_set || !(config_name == NULL))
  1272.         config = fopen(config_name,"rt");   /* Open the config file. */
  1273.     if (config == NULL && !config_name_set) /* Did it work? */
  1274.     {                                        /* Nope. */
  1275.         path_name(argv0,config_name,NULL);    /* Get path of POLYXARC.EXE. */
  1276.         strcat(config_name,DEFAULT_NAME);    /* Add the default .CFG name. */
  1277.         config = fopen(config_name,"rt");   /* Now try to open it. */
  1278.         if (config == NULL)                 /* Did that work? */
  1279.         {                                    /* Nope. */
  1280.  
  1281. /* Set up the config name to default to the default name in the current
  1282.    directory, then try to find a PENGUIN file. If there is a penguin file,
  1283.    it overrides the default. Otherwise, we'll try the default. */
  1284.             strcpy(config_name,DEFAULT_NAME);     /* Restore the default. */
  1285.             if (p_env())                         /* Get PENGUIN file, if any. */
  1286.                 config = fopen(config_name,"rt");/* Try to open it. */
  1287.         }
  1288.     }
  1289.     if (config == NULL)                     /* Nothing worked! */
  1290.     {
  1291.         printf("Cannot open configuration file %s.\n",config_name);
  1292.         return(1);
  1293.     }
  1294.     arc_list =                                /* Pre-allocate 1. */
  1295.             (ARC_TYPE *)malloc(sizeof(ARC_TYPE));
  1296.     signature_list =                        /* Pre-allocate 1. */
  1297.             (SIGNATURE_TYPE *)malloc(sizeof(SIGNATURE_TYPE));
  1298.     do    /* Do until we run out of file. */
  1299.     {
  1300.         i = TRUE;                            /* Initialize this flag. */
  1301.         do                                    /* Do until i == FALSE. */
  1302.         {
  1303.             f = fgets(oneline,sizeof(oneline),config);    /* Get one line. */
  1304.             if (f == NULL)                    /* Did it work? */
  1305.                 i = FALSE;                    /* If not, drop through. */
  1306.             else
  1307.             {
  1308.                 if ((g = parmtok(oneline," \t\n")) != NULL)/* Tokenize the line. */
  1309.                 {
  1310.                     if (stricmp(g,"BEGIN") == 0)/* First word BEGIN? */
  1311.                     {
  1312.                         g = parmtok(NULL," \t\n");       /* If so, tokenize again. */
  1313.                         if (stricmp(g,"POLYXARC") == 0) /* Second word POLYXARC? */
  1314.                             i = FALSE;            /* If so, drop through. */
  1315.                     }
  1316.                 }
  1317.             }
  1318.         } while (i);                        /* Do this until i == FALSE. */
  1319.         quit = 0;                            /* Don't quit yet! */
  1320.         do                                    /* Do until quit. */
  1321.         {
  1322.             f = fgets(oneline,sizeof(oneline),config);    /* Get a line. */
  1323.             if (f != NULL)                                /* Did it work? */
  1324.             {                                            /* Yup. */
  1325.                 if ((g = parmtok(oneline," \t\n")) != NULL)/* Tokenize. */
  1326.                 {
  1327. /* ARC level command_template */
  1328.                     if (strnicmp(g,"ARC",3) == 0)           /* First word ARC? */
  1329.                     {                                        /* Yup. */
  1330.                         arc6 = ('6' == g[3]);
  1331.                         i = atoi(parmtok(NULL," \t\n"));/* Get ARC level. */
  1332.                         s2 = parmtok(NULL," \t\n");     /* Get overwrite switch. */
  1333.                         s3 = parmtok(NULL," \t\n");     /* Get extract switch. */
  1334.                         g = parmtok(NULL,"\n");         /* Get the template. */
  1335.                         if (g == NULL)            /* Did we get all those fields? */
  1336.                         {                        /* Nope, something was left out. */
  1337.                             printf("Syntax error in config file.\n");
  1338.                             return(7);
  1339.                         }
  1340.                         while (*g == ' ' || *g == '\t') /* Remove leading spaces. */
  1341.                             ++g;
  1342.                         arc_list =                        /* Allocate room for more. */
  1343.                                 (ARC_TYPE *)realloc(arc_list,
  1344.                                         (arc_count + 1) * sizeof(ARC_TYPE));
  1345.                         arc_list[arc_count].type = (byte)i;        /* Save ARC level. */
  1346.                         arc_list[arc_count].version = (byte)arc6;/* Record if version 6. */
  1347.                         strcpy(arc_list[arc_count].overwrite,    /* Save overwrite. */
  1348.                                                 strtrunc(s2,OVER_LEN - 1));
  1349.                         strcpy(arc_list[arc_count].extract, /* Save extract. */
  1350.                                                 strtrunc(s3,EXTR_LEN - 1));
  1351.                         arc_list[arc_count].cmdline =    /* Allocate for template. */
  1352.                                                 malloc(strlen(g) + 1);
  1353.                         strcpy(arc_list[arc_count].cmdline,g);    /* Save template. */
  1354.                         ++arc_count;                            /* Count up. */
  1355.                     }
  1356. /* SIGNATURE offset signature command_template */
  1357.                     else if (stricmp(g,"SIGNATURE") == 0)   /* First word SIGNATURE? */
  1358.                     {                                        /* Yup. */
  1359.                         l = atol(parmtok(NULL," \t\n"));/* Get ARC level. */
  1360.                         s1 = parmtok(NULL," \t\n");     /* Get signature string. */
  1361.                         s2 = parmtok(NULL," \t\n");     /* Get overwrite switch. */
  1362.                         s3 = parmtok(NULL," \t\n");     /* Get extract switch. */
  1363.                         g = parmtok(NULL,"\n");          /* Get the template. */
  1364.                         if (g == NULL)            /* Did we get all those fields? */
  1365.                         {                        /* Nope, something was left out. */
  1366.                             printf("Syntax error in config file.\n");
  1367.                             return(7);
  1368.                         }
  1369.                         while (*g == ' ' || *g == '\t') /* Remove leading spaces. */
  1370.                             ++g;
  1371.                         signature_list =                /* Allocate room for more. */
  1372.                                 (SIGNATURE_TYPE *)realloc(signature_list,
  1373.                                 (signature_count + 1) * sizeof(SIGNATURE_TYPE));
  1374.                         signature_list[signature_count].file_offset = l;/* Save offset. */
  1375.                         strcpy(signature_list[signature_count].signature,/* Save signature. */
  1376.                                                 strtrunc(s1,SIGN_LEN - 1));
  1377.                         strcpy(signature_list[signature_count].overwrite,/* Save overwrite. */
  1378.                                                 strtrunc(s2,OVER_LEN - 1));
  1379.                         strcpy(signature_list[signature_count].extract, /* Save extract. */
  1380.                                                 strtrunc(s3,EXTR_LEN - 1));
  1381.                         signature_list[signature_count].cmdline =    /* Allocate for template. */
  1382.                                                 malloc(strlen(g) + 1);
  1383.                         strcpy(signature_list[signature_count].cmdline,g);    /* Save template. */
  1384.                         ++signature_count;                        /* Count up. */
  1385.                     }
  1386.                     else if (stricmp(g,"NOSORT") == 0)  /* First word NOSORT? */
  1387.                     {                                    /* Yup. */
  1388.                         nosort = TRUE;                /* Override sort directive. */
  1389.                     }
  1390.                     else if (stricmp(g,"END") == 0)     /* First word END? */
  1391.                     {                                    /* Yup. */
  1392.                         g = parmtok(NULL," \t\n");      /* Tokenize again. */
  1393.                         if (stricmp(g,"POLYXARC") == 0) /* Second word POLYXARC? */
  1394.                             quit = 1;                    /* Yup. Ignore everything
  1395.                                                        until end of file or
  1396.                                                        another BEGIN POLYXARC. */
  1397.                     }
  1398.                 }
  1399.             }
  1400.             else
  1401.                 quit = 2;    /* Reached end of file, really quit now. */
  1402.         } while (!quit);    /* Do until quit. */
  1403.     } while (quit < 2);     /* If quit == 1, not end of file, just end of
  1404.                                BEGIN/END block. Keep looking. */
  1405.     fclose(config);         /* End of file, close it. */
  1406.     return(0);                /* No errors, return 0. */
  1407. }
  1408.  
  1409. /****************************************************************************
  1410.                     This sorts the .ARC list by type.
  1411. ****************************************************************************/
  1412. void sort_arc_list(void)
  1413. {
  1414. ARC_TYPE local_arc_list;
  1415. register int i;
  1416. register int j;
  1417. register int k;
  1418. register int swapped;
  1419.  
  1420.     k = arc_count - 1;
  1421.     do
  1422.     {
  1423.         swapped = 0;
  1424.         for (i = 0; i < k; ++i)
  1425.         {
  1426.             j = arc_list[i].version - arc_list[i+1].version;
  1427.             if (j > 0 || (j == 0 && arc_list[i].type > arc_list[i+1].type))
  1428.             {
  1429.                 memcpy((char *)&local_arc_list,(char *)&arc_list[i],sizeof(ARC_TYPE));
  1430.                 memcpy((char *)&arc_list[i],(char *)&arc_list[i+1],sizeof(ARC_TYPE));
  1431.                 memcpy((char *)&arc_list[i+1],(char *)&local_arc_list,sizeof(ARC_TYPE));
  1432.                 swapped = 1;
  1433.             }
  1434.         }
  1435.         --k;
  1436.     } while (swapped);
  1437. }
  1438.  
  1439. /****************************************************************************
  1440.                     Process command-line parameters
  1441.  
  1442.   -#       Does not recognize a .MO? - .SU? file unless it ends
  1443.            with a decimal digit. Used only with the -F flag.
  1444.   -Cconf   Specify configuration file. Default is POLYXARC.CFG.
  1445.   -D       Will delete the archive if the extract was successful.
  1446.   -F[n]    Will process all Compressed Mail bundles found in Dir.
  1447.   -Maddr   Will calculate Compressed Mail bundle name to show the
  1448.            sending Net Address.  "addr" is YOUR Net/Node address.
  1449.   -N       Will NOT attempt to sort the archive prior to extract.
  1450.   -O       Overwrite.  Will NOT prompt if existing file is found.
  1451.   -R       Same as -O, included for ARCE syntax compatibility.
  1452.   -Q       QuietMode. Will NOT display the runtime configuration.
  1453.  
  1454. If -F is used then "Archive" MUST be a Path ONLY, not a Filename.
  1455. Use of -F forces -D and -O to be set TRUE and -N to be set False.
  1456. (The NOSORT verb in the configuration file overrides the -F parameter
  1457. and forces -N to be set True.) If a number is specified with the
  1458. -F parameter, then PolyXarc will only process that number of archives
  1459. (as a maximum).
  1460. ****************************************************************************/
  1461. void process_args(char *arg)
  1462. {
  1463. char    *tok;
  1464.  
  1465.     tok = strtok(arg,"\n");
  1466.     if (tok == NULL)
  1467.         return;
  1468.     if (*tok == '/' || *tok == '-')
  1469.     {
  1470.         ++tok;
  1471.         switch(toupper(*tok))
  1472.         {
  1473.           case '#': digits_only = TRUE;
  1474.                       break;
  1475.           case 'C': strcpy(config_name,strupr(++tok));
  1476.                     config_name_set = TRUE;
  1477.                     break;
  1478.           case 'D': delete_archives = TRUE;
  1479.                     break;
  1480.           case 'F': do_all_bundles = TRUE;
  1481.                     do_n_bundles = atoi(++tok);
  1482.                     break;
  1483.           case 'I': ignore_archiver_errors = TRUE;
  1484.                     break;
  1485.           case 'M': show_addresses = TRUE;
  1486.                     this_net = (unsigned int)atol(strtok(++tok,"/."));
  1487.                     this_node = (unsigned int)atol(strtok(NULL,"/."));
  1488.                     break;
  1489.           case 'N': nosort = TRUE;
  1490.                     break;
  1491.           case 'O':
  1492.           case 'R': overwrite = TRUE;
  1493.                     break;
  1494.           case 'Q': quiet = TRUE;
  1495.                     break;
  1496.         }
  1497.     }
  1498.     else
  1499.     {
  1500.         switch(name_count++)
  1501.         {
  1502.           case 0:    strcpy(archive_name,strupr(tok));    /* Get archive filespec. */
  1503.                     archive_name_set = TRUE;
  1504.                     break;
  1505.           case 1:    strcpy(extract_name,tok);            /* Get extract name filespec. */
  1506.                     break;
  1507.           default:    strcat(extract_name," ");           /* Add a space for parameter separation. */
  1508.                     strcat(extract_name,tok);            /* Get another extract name filespec. */
  1509.                     break;
  1510.         }
  1511.     }
  1512. }
  1513.  
  1514. /****************************************************************************
  1515.                     Display the run-time configuration.
  1516. ****************************************************************************/
  1517. void display_configuration(void)
  1518. {
  1519.     printf("Archive %s is %s.\n",
  1520.             do_all_bundles ? "path" : "name",
  1521.             archive_name_set ? (char *)archive_name : "the current directory");
  1522.     printf("Using configuration file %s.\n",config_name);
  1523.     printf("%s delete archives after extraction.\n",
  1524.                                             delete_archives ? "Will" : "Will not");
  1525.     if (do_all_bundles)
  1526.     {
  1527.         if (do_n_bundles)
  1528.             printf("Will process up to %d mail archives.\n",do_n_bundles);
  1529.         else
  1530.             printf("Will process all mail archives.\n");
  1531.     }
  1532.     printf("%s show source addresses.\n",show_addresses ? "Will" : "Will not");
  1533.     if (show_addresses)
  1534.         printf("This node's address is %u/%u.\n",this_net,this_node);
  1535.     printf("%s sort .ARC format archives.\n",nosort ? "Will not" : "Will");
  1536.     printf("%s automatically overwrite existing files.\n\n",
  1537.                                                 overwrite ? "Will" : "Will not");
  1538.     if (ignore_archiver_errors)
  1539.         printf("Will ignore errors from the archiver(s).\n");
  1540. }
  1541.  
  1542. /****************************************************************************
  1543.                               Main function.
  1544.  
  1545. Returns 0 if successful, or errorlevel for DOS on error:
  1546.    1 = Can't open/read source. (missing or invalid file)
  1547.    2 = Can't create/write dest (maybe disk or directory full)
  1548.    3 = Can't delete old source file. (read-only)
  1549.    4 = Can't rename temp file to source name.
  1550.    5 = Out of memory.
  1551.    6 = Cannot determine archive type.
  1552.    7 = Configuration file syntax error.
  1553.    8 = Non-0 error return from archive extractor.
  1554.    9 = Zero-length file.
  1555.   10 = Error while attempting to execute the archiver.
  1556. ****************************************************************************/
  1557. #ifdef OS_2
  1558. int main(int argc,char **argv,char **envp)
  1559. #else
  1560. int main(int argc,char **argv)
  1561. #endif
  1562. {
  1563. int     i;
  1564.  
  1565. #ifdef OS_2
  1566.     envp;                                /* Just to shut up /W3 warnings. */
  1567. #endif
  1568.  
  1569.     printf("\nPolyXarc v" R_VERS " " R_PORT "  " R_DATE "; Public Domain.\n");
  1570.     printf("Written by Jeffrey Nonken of 1:273/715@Fidonet (215)279-9799.\n\n");
  1571. #ifdef AMIGA
  1572.     printf("Ported to the Amiga by Steven M. Palm.  1:11/16 @ FidoNet\n");
  1573.     printf("Subject to change without notice.  No support implied.\n");
  1574.     printf("Just a reminder:  This uses MS-DOS style wildcards.\n\n");
  1575. #endif
  1576. #ifdef OS_2
  1577.     printf("Ported to OS/2 by Bill Andrus of 1:109/301@Fidonet.\n");
  1578. #endif
  1579.  
  1580.  
  1581.     if (argc == 2) if (stricmp(argv[1],"?") == 0)
  1582.     {
  1583.         printf("PolyXarc archive [-Cconf] [-D] [-F[n]] [-#] [-Maddr] [-N]\n"
  1584.                "                 [-O|R] [-Q] [-I] [filename [filename...]]\n"
  1585.                "See POLYXARC.DOC for more details.\n");
  1586.         quit(0);
  1587.     }
  1588.     for (i = 1; i < argc; ++i)        /* Process command-line arguments. */
  1589.         process_args(argv[i]);
  1590.     if (do_all_bundles)             /* If -f, override some settings. */
  1591.     {
  1592.         delete_archives = TRUE;     /* Delete archives when done. */
  1593.         overwrite = TRUE;            /* Overwrite existing files. */
  1594.         nosort = FALSE;             /* Don't sort. */
  1595.         slash(archive_name);        /* Add trailing backslash to path. */
  1596.     }
  1597. #ifndef AMIGA
  1598.     else  /* I said we don't do default extensions under AmigaDOS... ;^) */
  1599.         default_ext(archive_name,"*");  /* If no extension, add ".*". */
  1600. #endif
  1601.     i = get_config(argv[0]);        /* Get the configuration file. */
  1602.     if (!i)                         /* If it worked, keep going. */
  1603.     {
  1604.         sort_arc_list();            /* Sort the .ARC list by level. */
  1605.         if (!do_all_bundles         /* Doing bundles? */
  1606.             && !archive_name_set)    /* If not, needs a filename. */
  1607.         {                            /* No file name specified! Return error. */
  1608.             printf("Error: archive name not specified. Use 'PolyXarc ?' for help.\n");
  1609.             quit(1);
  1610.         }
  1611.         if (!quiet)                 /* If no -q, display runtime config. */
  1612.             display_configuration();
  1613.         i = discombobulate_all(archive_name,extract_name,do_all_bundles);
  1614.     }
  1615.     fcloseall();                    /* Close all files. */
  1616.  
  1617. #ifdef AMIGA
  1618.     quit(i);  /* for some reason, return() will not function as the */
  1619.           /* exit the main function prperly under Lattice 5.05. */
  1620.           /* It always gives 0.  I don't s'pose it would hurt to */
  1621.           /* do this on the IBM set as well.... */
  1622. #endif
  1623.  
  1624. /* Actually, you get a warning from the MSC compiler if you don't have
  1625.    a return statement in a function. I decided to leave the return in for
  1626.    MSC and TURBOC. jjn */
  1627.  
  1628. #ifdef IBM
  1629.     quit(i);
  1630.     return(i);
  1631. #endif
  1632. }
  1633.